package org.ehe.finance.subject.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.ehe.common.core.exception.BusinessException;
import org.ehe.common.core.utils.MapstructUtils;
import org.ehe.common.core.utils.StringUtils;
import org.ehe.common.mybatis.core.page.PageQuery;
import org.ehe.common.mybatis.core.page.TableDataInfo;
import org.ehe.common.satoken.utils.LoginHelper;
import org.ehe.finance.subject.domain.ErpFinanceAccountSubject;
import org.ehe.finance.subject.domain.bo.ErpFinanceAccountSubjectBo;
import org.ehe.finance.subject.domain.vo.ErpFinanceAccountSubjectTreeVo;
import org.ehe.finance.subject.domain.vo.ErpFinanceAccountSubjectVo;
import org.ehe.finance.subject.mapper.ErpFinanceAccountSubjectMapper;
import org.ehe.finance.subject.service.ErpFinanceAccountSubjectService;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 财务科目实现类
 * @author :zhangnn
 * @className :ErpFinanceAccountSubjectServiceImpl
 * @description: TODO
 * @date 2025-08-22 09:47:35
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ErpFinanceAccountSubjectServiceImpl extends ServiceImpl<ErpFinanceAccountSubjectMapper, ErpFinanceAccountSubject>
    implements ErpFinanceAccountSubjectService {
    /**
     * 分页
     * @param bo
     * @param pageQuery
     * @return
     */
    @Override
    public TableDataInfo<ErpFinanceAccountSubjectVo> queryPageList(ErpFinanceAccountSubjectBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<ErpFinanceAccountSubject> lqw = new LambdaQueryWrapper<>();
        lqw.like(StringUtils.isNotBlank(bo.getSubjectName()),ErpFinanceAccountSubject::getSubjectName,bo.getSubjectName());
        lqw.like(StringUtils.isNotBlank(bo.getSubjectCode()),ErpFinanceAccountSubject::getSubjectCode,bo.getSubjectCode());
        lqw.eq(null != bo.getSubjectType(),ErpFinanceAccountSubject::getSubjectType,bo.getSubjectType());

        IPage<ErpFinanceAccountSubjectVo> page = this.baseMapper.selectVoPage(pageQuery.build(), lqw);

        return TableDataInfo.build(page);
    }

    /**
     * 新增
     * @param bo
     * @return
     */
    @Override
    public boolean insertByBo(ErpFinanceAccountSubjectBo bo) {
        // 检查编码唯一性
        if (checkSubjectCodeUnique(bo.getSubjectCode(), null)) {
            throw new BusinessException("科目编码已存在，请更换");
        }

        String subjectCode = generateSubjectCode(bo.getParentId(), bo.getSubjectType());

        ErpFinanceAccountSubject entity = MapstructUtils.convert(bo, ErpFinanceAccountSubject.class);
        entity.setSubjectCode(subjectCode);
        ErpFinanceAccountSubject parent = getById(bo.getParentId());
        if(Objects.nonNull(parent)){
            entity.setSubjectLevel(parent.getSubjectLevel()+1);
        }
        // 设置租户ID，实际项目中从上下文获取
        entity.setTenantId(LoginHelper.getTenantId());
        // 设置是否末级科目
        //entity.setLeaf(bo.getIsLastLevel() == 1);
        return save(entity);
    }

    /**
     * 修改
     * @param bo
     * @return
     */
    @Override
    public boolean updateByBo(ErpFinanceAccountSubjectBo bo) {
        // 检查编码唯一性（排除自身）
        if (checkSubjectCodeUnique(bo.getSubjectCode(), bo.getId())) {
            throw new BusinessException("科目编码已存在，请更换");
        }

        ErpFinanceAccountSubject entity = MapstructUtils.convert(bo, ErpFinanceAccountSubject.class);
        //entity.setLeaf(bo.getIsLastLevel() == 1);
        return updateById(entity);
    }

    /**
     * 校验编码唯一
     * @param subjectCode
     * @param id
     * @return
     */
    @Override
    public boolean checkSubjectCodeUnique(String subjectCode, Long id) {
        LambdaQueryWrapper<ErpFinanceAccountSubject> query = new LambdaQueryWrapper<>();
        query.eq(ErpFinanceAccountSubject::getSubjectCode, subjectCode)
            .eq(ErpFinanceAccountSubject::getTenantId, LoginHelper.getTenantId())
            .eq(ErpFinanceAccountSubject::getDelFlag, "0"); // 只校验未删除的记录

        // 如果是更新，排除自身
        if (id != null) {
            query.ne(ErpFinanceAccountSubject::getId, id);
        }
        return count(query) > 0;
    }

    /**
     * 删除
     * @param id
     * @return
     */
    @Override
    public boolean deleteById(Long id) {
        // 1. 校验是否存在子科目（未删除的）
        LambdaQueryWrapper<ErpFinanceAccountSubject> childQuery = new LambdaQueryWrapper<>();
        childQuery.eq(ErpFinanceAccountSubject::getParentId, id)
            .eq(ErpFinanceAccountSubject::getDelFlag, "0"); // 只查未删除的子科目

        long childCount = count(childQuery);
        if (childCount > 0) {
            throw new BusinessException("当前科目存在子科目，不允许删除");
        }
        return removeById(id);
    }


    /**
     * 生成科目编码（根据父级科目自动生成）
     * @param parentId 父级科目ID（0表示一级科目）
     * @param subjectType 科目类型（1-资产，2-负债等，用于一级科目编码前缀）
     * @return 生成的subject_code
     */
    private String generateSubjectCode(Long parentId, Integer subjectType) {
        // 情况1：一级科目（parentId=0）
        if (parentId == 0 || parentId == null) {
            return generateLevel1Code(subjectType);
        }

        // 情况2：非一级科目（需基于父级编码生成）
        // 2.1 查询父级科目信息
        ErpFinanceAccountSubject parent = getById(parentId);
        if (parent == null || "1".equals(parent.getDelFlag())) {
            throw new BusinessException("父级科目不存在或已删除");
        }
        // 2.2 校验父级是否为末级（若为末级，需先改为非末级）
        if (parent.getLeaf()) {
            parent.setLeaf(false);
            updateById(parent); // 更新父级is_leaf为0
        }

        // 2.3 父级编码和层级
        String parentCode = parent.getSubjectCode();
        Integer parentLevel = parent.getSubjectLevel();
        Integer currentLevel = parentLevel + 1; // 当前层级=父级层级+1

        // 2.4 查询同一父级下所有未删除的子科目编码
        LambdaQueryWrapper<ErpFinanceAccountSubject> lqw = new LambdaQueryWrapper<>();
        lqw.eq(ErpFinanceAccountSubject::getParentId, parentId)
            .eq(ErpFinanceAccountSubject::getDelFlag, "0")
            .select(ErpFinanceAccountSubject::getSubjectCode);
        List<ErpFinanceAccountSubject> children = list(lqw);

        // 2.5 提取最大序号并加1
        int maxSeq = 0;
        for (ErpFinanceAccountSubject child : children) {
            String childCode = child.getSubjectCode();
            // 子科目编码=父级编码+2位序号 → 截取后2位作为序号
            String seqStr = childCode.substring(parentCode.length());
            int seq = Integer.parseInt(seqStr);
            if (seq > maxSeq) {
                maxSeq = seq;
            }
        }
        int newSeq = maxSeq + 1;

        // 2.6 序号补0成2位（如1→01，10→10）
        String seqStr = String.format("%02d", newSeq);

        // 2.7 生成当前科目编码（父级编码+新序号）
        String currentCode = parentCode + seqStr;

        // 校验编码长度是否符合层级（如level=2必须6位）
        int expectedLength = 4 + 2 * (currentLevel - 1);
        if (currentCode.length() != expectedLength) {
            throw new BusinessException("编码生成错误，层级与长度不匹配");
        }
        return currentCode;
    }


    /**
     * 生成一级科目编码（4位）
     * 规则：首位为科目类型（1-5），后3位按同类型最大序号递增
     */
    private String generateLevel1Code(Integer subjectType) {
        // 校验科目类型合法性
        if (subjectType < 1 || subjectType > 5) {
            throw new BusinessException("科目类型错误（1-5）");
        }

        // 查询同类型的一级科目（parent_id=0，level=1）
        LambdaQueryWrapper<ErpFinanceAccountSubject> lqw = new LambdaQueryWrapper<>();
        lqw.eq(ErpFinanceAccountSubject::getParentId, 0)
            .eq(ErpFinanceAccountSubject::getSubjectLevel, 1)
            .eq(ErpFinanceAccountSubject::getSubjectType, subjectType)
            .eq(ErpFinanceAccountSubject::getDelFlag, "0")
            .select(ErpFinanceAccountSubject::getSubjectCode);
        List<ErpFinanceAccountSubject> level1Subjects = list(lqw);

        // 提取最大后3位序号（一级编码格式：类型位+3位序号，如1001→类型1，序号001）
        int maxSeq = 0;
        for (ErpFinanceAccountSubject subject : level1Subjects) {
            String code = subject.getSubjectCode();
            // 截取后3位（如1001→001）
            String seqStr = code.substring(1);
            int seq = Integer.parseInt(seqStr);
            if (seq > maxSeq) {
                maxSeq = seq;
            }
        }
        int newSeq = maxSeq + 1;

        // 生成4位编码（类型位+3位序号，如类型1+序号5→1005）
        return subjectType + String.format("%03d", newSeq);
    }


    /**
     * 获取所有科目树形结构
     */
    @Override
    public List<ErpFinanceAccountSubjectTreeVo> getAllSubjectTree() {
        // 1. 查询所有未删除的科目
        LambdaQueryWrapper<ErpFinanceAccountSubject> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ErpFinanceAccountSubject::getDelFlag, "0")
            .orderByAsc(ErpFinanceAccountSubject::getSubjectCode); // 按编码排序，保证层级顺序
        List<ErpFinanceAccountSubject> allSubjects = list(queryWrapper);

        // 2. 转换为树形VO
        return buildSubjectTree(allSubjects);
    }

    /**
     * 根据父级ID获取子科目树形结构
     */
    @Override
    public List<ErpFinanceAccountSubjectTreeVo> getChildSubjectTree(Long parentId) {
        if (parentId == null) {
            parentId = 0L;
        }

        // 1. 查询指定父级下的所有未删除科目（包含子级的子级）
        LambdaQueryWrapper<ErpFinanceAccountSubject> queryWrapper = new LambdaQueryWrapper<>();
        Long finalParentId = parentId;
        queryWrapper.eq(ErpFinanceAccountSubject::getDelFlag, "0")
            .and(wrapper -> wrapper.eq(ErpFinanceAccountSubject::getParentId, finalParentId)
                .or()
                .apply("find_in_set({0}, get_parent_ids(id))", finalParentId))
            .orderByAsc(ErpFinanceAccountSubject::getSubjectCode);

        List<ErpFinanceAccountSubject> subjects = list(queryWrapper);

        // 2. 构建树形结构
        return buildSubjectTree(subjects);
    }

    /**
     * 构建科目树形结构
     */
    private List<ErpFinanceAccountSubjectTreeVo> buildSubjectTree(List<ErpFinanceAccountSubject> subjects) {
        // 1. 转换为TreeVo并分组
        List<ErpFinanceAccountSubjectTreeVo> treeVos = subjects.stream()
            .map(this::convertToTreeVo)
            .collect(Collectors.toList());

        // 2. 按parentId分组
        Map<Long, List<ErpFinanceAccountSubjectTreeVo>> groupByParentId = treeVos.stream()
            .collect(Collectors.groupingBy(ErpFinanceAccountSubjectTreeVo::getParentId));

        // 3. 递归设置子节点
        List<ErpFinanceAccountSubjectTreeVo> rootNodes = groupByParentId.getOrDefault(0L, new ArrayList<>());
        rootNodes.forEach(node -> setChildren(node, groupByParentId));

        return rootNodes;
    }

    /**
     * 为节点设置子节点
     */
    private void setChildren(ErpFinanceAccountSubjectTreeVo node, Map<Long, List<ErpFinanceAccountSubjectTreeVo>> groupByParentId) {
        List<ErpFinanceAccountSubjectTreeVo> children = groupByParentId.getOrDefault(node.getId(), new ArrayList<>());
        if (!children.isEmpty()) {
            node.setChildren(children);
            // 递归设置子节点的子节点
            children.forEach(child -> setChildren(child, groupByParentId));
        }
    }

    /**
     * 转换实体为树形VO
     */
    private ErpFinanceAccountSubjectTreeVo convertToTreeVo(ErpFinanceAccountSubject subject) {
        ErpFinanceAccountSubjectTreeVo treeVo = new ErpFinanceAccountSubjectTreeVo();
        // 复制基本字段
        treeVo.setId(subject.getId());
        treeVo.setSubjectCode(subject.getSubjectCode());
        treeVo.setSubjectName(subject.getSubjectName());
        treeVo.setParentId(subject.getParentId());
        treeVo.setSubjectLevel(subject.getSubjectLevel());
        treeVo.setSubjectType(subject.getSubjectType());
        treeVo.setBalanceDirection(subject.getBalanceDirection());
        treeVo.setLeaf(subject.getLeaf());
        treeVo.setCash(subject.getCash());
        treeVo.setBank(subject.getBank());
        treeVo.setAuxiliaryAccounting(subject.getAuxiliaryAccounting());
        treeVo.setStatus(subject.getStatus());

        // 设置类型名称（便于前端展示）
        treeVo.setSubjectTypeName(getSubjectTypeName(subject.getSubjectType()));
        treeVo.setBalanceDirectionName(getBalanceDirectionName(subject.getBalanceDirection()));
        treeVo.setStatusName(subject.getStatus() == 1 ? "启用" : "禁用");

        return treeVo;
    }

    /**
     * 获取科目类型名称
     */
    private String getSubjectTypeName(Integer type) {
        if (type == null) {
            return "";
        }
        switch (type) {
            case 1: return "资产";
            case 2: return "负债";
            case 3: return "所有者权益";
            case 4: return "收入";
            case 5: return "费用";
            default: return "未知";
        }
    }

    /**
     * 获取余额方向名称
     */
    private String getBalanceDirectionName(Integer direction) {
        if (direction == null) {
            return "";
        }
        return direction == 1 ? "借方" : "贷方";
    }
}
